home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / machserver / 1.098 / rpc / rpcCall.c < prev    next >
C/C++ Source or Header  |  1991-07-29  |  25KB  |  833 lines

  1. /*
  2.  * rpcCall.c --
  3.  *
  4.  *      These are the top-level routines for the client side of Remote
  5.  *      Procedure Call - the routines do overhead tasks like setting up a
  6.  *      message, and managing client channels.  The network protocol for
  7.  *      the client side is in rpcClient.c.
  8.  *
  9.  * Copyright 1985 Regents of the University of California
  10.  * All rights reserved.
  11.  */
  12.  
  13. #ifndef lint
  14. static char rcsid[] = "$Header: /sprite/src/kernel/rpc/RCS/rpcCall.c,v 9.13 90/10/09 11:58:04 jhh Exp $ SPRITE (Berkeley)";
  15. #endif /* not lint */
  16.  
  17.  
  18. #include <sprite.h>
  19. #include <stdio.h>
  20.  
  21. #include <rpc.h>
  22. #include <rpcPacket.h>
  23. #include <rpcClient.h>
  24. #include <rpcTrace.h>
  25. #include <rpcHistogram.h>
  26. #include <sys.h>
  27. #include <timerTick.h>
  28. #include <timer.h>
  29. /* Not needed if recov tracing is removed. */
  30. #include <recov.h>
  31.  
  32. /*
  33.  * So we can print out rpc names we include the rpcServer definitions.
  34.  */
  35. #include <rpcServer.h>
  36.  
  37. /*
  38.  * The client channel table is kept as an array of pointers to channels.
  39.  * rpcClient.h describes the contents of a ClientChannel.  The number
  40.  * of channels limits the parallelism available on the client.  During
  41.  * system shutdown, for example, there may be many processes doing
  42.  * remote operations (removing swap files) at the same time.
  43.  */
  44.  
  45. RpcClientChannel **rpcChannelPtrPtr = (RpcClientChannel **)NIL;
  46. int           rpcNumChannels = 8;
  47. int           numFreeChannels = 8;
  48.  
  49. /*
  50.  * The allocation and freeing of channels is monitored.
  51.  * A process might have to wait for a free RPC channel.
  52.  */
  53. Sync_Condition freeChannels;
  54. Sync_Semaphore rpcMutex = Sync_SemInitStatic("Rpc:rpcMutex");
  55.  
  56. /*
  57.  * There is sequence of rpc transaction ids that increases over time.
  58.  */
  59.  
  60. unsigned int rpcID = 1;
  61.  
  62. /*
  63.  * A boottime Id is used to help servers realize that a client has
  64.  * rebooted since it talked to it last.  It is zero for the first
  65.  * RPC, one that gets the time.  Then it is set to that time and does
  66.  * not change until we reboot.
  67.  */
  68.  
  69. unsigned int rpcBootID = 0;
  70. /*
  71.  * A count is kept of the number of RPCs made.
  72.  * The zero'th element is used to count attempts at
  73.  * RPCs with unknown RPC numbers - RPC number 0 is unused.
  74.  */
  75. int rpcClientCalls[RPC_LAST_COMMAND+1];
  76.  
  77. /*
  78.  * For one of the client policies for handling negative acknowledgements,
  79.  * we ramp down the number of channels used with the ailing server.  These
  80.  * data structures keep track of which servers are unhappy.
  81.  */
  82. typedef struct    UnhappyServer {
  83.     int        serverID;
  84.     Timer_Ticks    time;
  85. } UnhappyServer;
  86.  
  87. UnhappyServer    serverAllocState[8];
  88.  
  89. /* Initial back-off interval for negative acknowledgements. */
  90. unsigned int    channelStateInterval;
  91.  
  92. /* forward declaration */
  93. static Boolean GetChannelAllocState _ARGS_((int serverID, Timer_Ticks *time));
  94. static void SetChannelAllocState _ARGS_((int serverID, Boolean trouble));
  95. static void SetChannelAllocStateInt _ARGS_((int serverID, Boolean trouble));
  96.  
  97.  
  98. /*
  99.  *----------------------------------------------------------------------
  100.  *
  101.  * Rpc_Call --
  102.  *
  103.  *      Top-level interface for a client that makes a remote procedure
  104.  *      call.  Our caller has to pre-allocate all storage for the data
  105.  *      going to the server in the request message, and for the data
  106.  *      returning in the reply message.  The storage argument contains
  107.  *      pointers and sizes for this preallocated space.  Upon return
  108.  *    the data from the reply message will be in the specified storage
  109.  *    areas.
  110.  *
  111.  * Results:
  112.  *      An error code that either reflects an error in delivery/transport
  113.  *      or is an error code from the remote procedure.  Also, the storage
  114.  *      input specification is modified - the return parameter and data
  115.  *      size fields are updated to reflect the true size of the return
  116.  *      parameter and data blocks.  Finally, the return parameter and data
  117.  *      areas contain the results of the remote procedure call.
  118.  *
  119.  * Side effects:
  120.  *    There are no side effects on this machine except that some addressing
  121.  *    information is kept as a hint for future RPCs.  The semantics of
  122.  *    the remote procedure are unlimited on the server machine.
  123.  *
  124.  *----------------------------------------------------------------------
  125.  */
  126. ReturnStatus
  127. Rpc_Call(serverID, command, storagePtr)
  128.     int serverID;        /* Indicates the server for the RPC.  The
  129.                  * special value SERVER_BROADCAST is used
  130.                  * to specify a broadcast RPC.  The first
  131.                  * reply received is returned and subsequent
  132.                  * replies get discarded.  Broadcast RPCs
  133.                  * are NOT retried if there is no response. */
  134.     int command;        /* Rpc command.  Values defined in rpcCall.h */
  135.     Rpc_Storage *storagePtr;    /* Specifies buffer areas for request and
  136.                  * reply messages. */
  137. {
  138.     register RpcClientChannel *chanPtr;    /* Handle for communication channel */
  139.     register ReturnStatus error;    /* General error return status */
  140.     Time histTime;            /* Time for histogram taking */
  141.     unsigned    int srvBootID;        /* Boot time stamp from server, used to
  142.                      * track server reboots */
  143.     Boolean notActive = 0;        /* Not active flag from server */
  144.  
  145.     if (serverID < 0 || serverID >= NET_NUM_SPRITE_HOSTS) {
  146.     printf("Rpc_Call, bad serverID <%d>\n", serverID);
  147.     return(GEN_INVALID_ARG);
  148.     } else if (serverID != RPC_BROADCAST_SERVER_ID &&
  149.            serverID == rpc_SpriteID) {
  150.     printf("Rpc_Call: Trying RPC #%d to myself\n", command);
  151.     return(GEN_INVALID_ARG);
  152.     } else if ((serverID == RPC_BROADCAST_SERVER_ID) &&
  153.            ! (command == RPC_FS_PREFIX ||
  154.           command == RPC_GETTIME)) {
  155.     panic("Trying to broadcast a non-prefix RPC");
  156.     return(GEN_INVALID_ARG);
  157.     }
  158. #ifdef TIMESTAMP
  159.     RPC_NIL_TRACE(RPC_CLIENT_A, "Rpc_Call");
  160. #endif /* TIMESTAMP */
  161.  
  162. allocAgain:
  163.     RPC_CALL_TIMING_START(command, &histTime);
  164.     chanPtr = RpcChanAlloc(serverID);
  165.  
  166. #ifdef TIMESTAMP
  167.     RPC_NIL_TRACE(RPC_CLIENT_B, "alloc");
  168. #endif /* TIMESTAMP */
  169.     /*
  170.      * Initialize the RPC request message header and put buffer
  171.      * specifications of our caller into the state of the channel.  This
  172.      * is once-per-rpc initialization that does not need to be repeated if
  173.      * we have to re-send.
  174.      */
  175.     RpcSetup(serverID, command, storagePtr, chanPtr);
  176.  
  177.     /*
  178.      * Update a histogram of RPCs made.  The zeroth element is used to
  179.      * count unknown rpcs
  180.      */
  181.     if (command > 0 &&
  182.     command <= RPC_LAST_COMMAND) {
  183.     rpcClientCalls[command]++;
  184.     } else {
  185.     /*
  186.      */
  187.     printf("Rpc_Call: unknown rpc command (%d)\n", command);
  188.     rpcClientCalls[0]++;    /* 0 == RPC_BAD_COMMAND */
  189.     }
  190. #ifdef TIMESTAMP
  191.     RPC_NIL_TRACE(RPC_CLIENT_C, "setup");
  192. #endif /* TIMESTAMP */
  193.  
  194.     /*
  195.      * Call RpcDoCall, which synchronizes with RpcClientDispatch,
  196.      * to do the send-receive-timeout loop for the RPC.
  197.      */
  198. /* Remove this debugging print stuff soon. */
  199.     if (command == RPC_ECHO_2 && recov_PrintLevel >= RECOV_PRINT_ALL) {
  200.     Sys_HostPrint(serverID, "Pinging server\n");
  201.     }
  202.     error = RpcDoCall(serverID, chanPtr, storagePtr, command,
  203.               &srvBootID, ¬Active);
  204.     RpcChanFree(chanPtr);
  205.     
  206. #ifdef TIMESTAMP
  207.     RPC_NIL_TRACE(RPC_CLIENT_OUT, "return");
  208. #endif /* TIMESTAMP */
  209.  
  210.     RPC_CALL_TIMING_END(command, &histTime);
  211. /* This slow printing stuff should be removed soon. It's for debugging. */
  212.     if (command == RPC_ECHO_2 && recov_PrintLevel >= RECOV_PRINT_ALL) {
  213.     if (error == RPC_NACK_ERROR) {
  214.         Sys_HostPrint(serverID,
  215.             "Ping result bad: Nack error from server\n");
  216.     } else if (error == RPC_TIMEOUT || error == NET_UNREACHABLE_NET) {
  217.         Sys_HostPrint(serverID, "Ping result bad: server is dead.\n");
  218.     }
  219.     if (error == SUCCESS) {
  220.         Sys_HostPrint(serverID, "Pinged serverID successfully.\n");
  221.     }
  222.     }
  223.     if (error == RPC_NACK_ERROR) {
  224.     /*
  225.      * This error is only returned if the client policy for handling
  226.      * negative acknowledgements is to ramp down the number of channels
  227.      * used, so that's what we do.
  228.      */
  229.     SetChannelAllocState(serverID, TRUE);
  230.     goto allocAgain;
  231.     }
  232. #ifndef NO_RECOVERY
  233.     if (error == RPC_TIMEOUT || error == NET_UNREACHABLE_NET) {
  234.     if (command != RPC_ECHO_2) {
  235.         printf("<%s> ", rpcService[command].name);
  236.         Sys_HostPrint(serverID, "RPC timed-out\n");
  237.     }
  238.     Recov_HostDead(serverID);
  239.     } else {
  240.     Recov_HostAlive(serverID, srvBootID, TRUE, notActive);
  241.     }
  242. #endif /* NO_RECOVERY */
  243.     return(error);
  244. }
  245.  
  246. /*
  247.  *----------------------------------------------------------------------
  248.  *
  249.  * RpcSetup --
  250.  *
  251.  *      Initialize the RPC header for the request message and set up the
  252.  *      buffer specifications for the request and reply messages.  The
  253.  *      operations done here are done once before the request message is
  254.  *      sent out the first time, and they do not have to be re-done if the
  255.  *      request message needs to be re-sent.
  256.  *
  257.  * Results:
  258.  *    None.
  259.  *
  260.  * Side effects:
  261.  *      The Rpc header is initialized.  The buffer specifications for the
  262.  *      request and reply messages are set up.
  263.  *
  264.  *----------------------------------------------------------------------
  265.  */
  266. void
  267. RpcSetup(serverID, command, storagePtr, chanPtr)
  268.     int serverID;            /* The server for the RPC */
  269.     int command;            /* The RPC to perform */
  270.     register Rpc_Storage *storagePtr;    /* Specifies storage for the RPC
  271.                      * parameters */
  272.     register RpcClientChannel *chanPtr;    /* The channel for the RPC */
  273. {
  274.     register Net_ScatterGather *bufferPtr;    /* This specifies a part of the
  275.                          * packet to the network driver
  276.                          */
  277.     register RpcHdr *rpcHdrPtr;            /* The RPC header */
  278.  
  279.     /*
  280.      * Initialize the RPC header for the request message.  A couple fields
  281.      * are set up elsewhere.  The server hint is left over from previous
  282.      * RPCs.  The channel ID and the version number are set up at
  283.      * boot time by Rpc_Init.
  284.      */
  285.     rpcHdrPtr = (RpcHdr *) &chanPtr->requestRpcHdr;
  286.     if(command == RPC_ECHO_1) {
  287.     rpcHdrPtr->flags = RPC_ECHO;
  288.     } else {
  289.     rpcHdrPtr->flags = RPC_REQUEST;
  290.     }
  291.     rpcHdrPtr->flags |= RPC_SERVER;
  292.  
  293.     rpcHdrPtr->clientID = rpc_SpriteID;
  294.     rpcHdrPtr->serverID = serverID;
  295.     rpcHdrPtr->bootID = rpcBootID;
  296.     rpcHdrPtr->ID = rpcID++;
  297.     rpcHdrPtr->command = command;
  298.  
  299.     /*
  300.      * Setup the timeout parameters depending on the route to the server.
  301.      * This is rather simple minded.
  302.      */
  303.     if (chanPtr->constPtr == (RpcConst *)NIL) {
  304.     Net_Route *routePtr = Net_IDToRoute(serverID, 0, FALSE, 
  305.                 (Sync_Semaphore *) NIL, 0);
  306.     if (routePtr != (Net_Route *)NIL &&
  307.         routePtr->protocol == NET_PROTO_INET) {
  308.         chanPtr->constPtr = &rpcInetConst;
  309.     } else {
  310.         chanPtr->constPtr = &rpcEtherConst;
  311.     }
  312.     if (routePtr != (Net_Route *)NIL) {
  313.         Net_ReleaseRoute(routePtr);
  314.     }
  315.     }
  316.     /*
  317.      * Copy buffer pointers into the state of the channel.
  318.      */
  319.     bufferPtr        = &chanPtr->request.paramBuffer;
  320.     bufferPtr->bufAddr    = storagePtr->requestParamPtr;
  321.     bufferPtr->length    = storagePtr->requestParamSize;
  322.  
  323.     rpcHdrPtr->paramSize = storagePtr->requestParamSize;
  324.  
  325.     bufferPtr        = &chanPtr->request.dataBuffer;
  326.     bufferPtr->bufAddr    = storagePtr->requestDataPtr;
  327.     bufferPtr->length    = storagePtr->requestDataSize;
  328.  
  329.     rpcHdrPtr->dataSize = storagePtr->requestDataSize;
  330.  
  331.     bufferPtr        = &chanPtr->reply.paramBuffer;
  332.     bufferPtr->bufAddr    = storagePtr->replyParamPtr;
  333.     bufferPtr->length    = storagePtr->replyParamSize;
  334.  
  335.     bufferPtr        = &chanPtr->reply.dataBuffer;
  336.     bufferPtr->bufAddr    = storagePtr->replyDataPtr;
  337.     bufferPtr->length    = storagePtr->replyDataSize;
  338.  
  339.     /*
  340.      * Reset state about the reception of replies.
  341.      */
  342.     chanPtr->actualDataSize = 0;
  343.     chanPtr->actualParamSize = 0;
  344.     chanPtr->fragsReceived = 0;
  345.     chanPtr->fragsDelivered = 0;
  346. }
  347. #ifdef DEBUG
  348. #define CHAN_TRACESIZE 1000
  349. #define INC(ctr) { (ctr) = ((ctr) == CHAN_TRACESIZE-1) ? 0 : (ctr)+1; }
  350. typedef struct {
  351.     RpcClientChannel    *chanPtr;
  352.     char        *action;
  353.     int            pNum;
  354.     int            serverID;
  355.     int            chanNum;
  356. } debugElem;
  357.  
  358. static debugElem    debugArray[CHAN_TRACESIZE];
  359. static int         debugCtr;
  360. #define CHAN_TRACE(channel, string) \
  361. {    \
  362.     debugElem *ptr = &debugArray[debugCtr];    \
  363.     INC(debugCtr);    \
  364.     ptr->chanPtr = (channel);    \
  365.     ptr->action = string;    \
  366.     ptr->chanNum = (channel)->index;    \
  367.     ptr->serverID = (channel)->serverID;    \
  368.     ptr->pNum = Mach_GetProcessorNumber();    \
  369. }    
  370. #else
  371. #define CHAN_TRACE(channel, string)
  372. #endif
  373.  
  374.  
  375.  
  376.  
  377. /*
  378.  *----------------------------------------------------------------------
  379.  *
  380.  * GetChannelAllocState --
  381.  *
  382.  *    Get the state of channel allocation in regards to a certain server.
  383.  *
  384.  * Results:
  385.  *    True if the server is marked as being congested.  False otherwise.
  386.  *
  387.  * Side effects:
  388.  *    None.
  389.  *
  390.  *----------------------------------------------------------------------
  391.  */
  392. static Boolean
  393. GetChannelAllocState(serverID, time)
  394.     int        serverID;
  395.     Timer_Ticks    *time;
  396. {
  397.     int        i;
  398.     Boolean    found = FALSE;
  399.  
  400.     for (i = 0; i < (sizeof (serverAllocState) / sizeof (UnhappyServer)); i++) {
  401.     if (serverAllocState[i].serverID == serverID) {
  402.         found = TRUE;
  403.         break;
  404.     }
  405.     }
  406.     if (!found) {
  407.     return FALSE;
  408.     }
  409.     *time = serverAllocState[i].time;
  410.     return TRUE;
  411. }
  412.  
  413.  
  414.  
  415. /*
  416.  *----------------------------------------------------------------------
  417.  *
  418.  * SetChannelAllocState--
  419.  *
  420.  *    Set the state of channel allocation in regards to a certain server.
  421.  *    If we've been getting "noAllocs" back from a server, we want to
  422.  *    ramp down our use of it by using fewer client channels with it.
  423.  *    This routine includes a master lock around it for places where
  424.  *    it's called unprotected.
  425.  *
  426.  * Results:
  427.  *    None.
  428.  *
  429.  * Side effects:
  430.  *    None.
  431.  *
  432.  *----------------------------------------------------------------------
  433.  */
  434. static ENTRY void
  435. SetChannelAllocState(serverID, trouble)
  436.     int        serverID;
  437.     Boolean    trouble;
  438. {
  439.  
  440.     MASTER_LOCK(&rpcMutex);
  441.  
  442.     SetChannelAllocStateInt(serverID, trouble);
  443.  
  444.     MASTER_UNLOCK(&rpcMutex);
  445.  
  446.     return;
  447. }
  448.  
  449.  
  450. /*
  451.  *----------------------------------------------------------------------
  452.  *
  453.  * SetChannelAllocStateInt --
  454.  *
  455.  *    Set the state of channel allocation in regards to a certain server.
  456.  *    If we've been getting "noAllocs" back from a server, we want to
  457.  *    ramp down our use of it by using fewer client channels with it.
  458.  *
  459.  * Results:
  460.  *    None.
  461.  *
  462.  * Side effects:
  463.  *    None.
  464.  *
  465.  *----------------------------------------------------------------------
  466.  */
  467. static void
  468. SetChannelAllocStateInt(serverID, trouble)
  469.     int        serverID;
  470.     Boolean    trouble;
  471. {
  472.     int        i;
  473.     Boolean    found = FALSE;
  474.  
  475.  
  476.     for (i = 0; i < (sizeof (serverAllocState) / sizeof (UnhappyServer)); i++) {
  477.     if (serverAllocState[i].serverID == serverID) {
  478.         /* already marked as unhappy */
  479.         found = TRUE;
  480.         break;
  481.     }
  482.     }
  483.     if (!found && !trouble) {
  484.     /* server isn't already marked as being in trouble. */
  485.     return;
  486.     }
  487.     if (!found) {
  488.     /* Server is in trouble, we need to record this. */
  489.     for (i = 0; i < (sizeof (serverAllocState) / sizeof (UnhappyServer));
  490.         i++) {
  491.         if (serverAllocState[i].serverID == -1) {
  492.         found = TRUE;
  493.         break;
  494.         }
  495.     }
  496.     if (!found) {
  497.         /* No more spaces to mark unhappy server! */
  498.         rpcCltStat.noMark++;
  499.         printf("SetChannelAllocStateInt: %s\n",
  500.             "No more room to keep track of congested servers.");
  501.         return;
  502.     }
  503.     }
  504.     if (trouble) {
  505.     Timer_GetCurrentTicks(&(serverAllocState[i].time));
  506.     if (serverAllocState[i].serverID == -1) {
  507.         rpcCltStat.newTrouble++;
  508.     } else {
  509.         rpcCltStat.moreTrouble++;
  510.     }
  511.     serverAllocState[i].serverID = serverID;
  512.     } else {
  513.     rpcCltStat.endTrouble++;
  514.     serverAllocState[i].serverID = -1;
  515.     }
  516.  
  517.     return;
  518. }
  519.  
  520.  
  521. /*
  522.  *----------------------------------------------------------------------
  523.  *
  524.  * RpcChanAlloc --
  525.  *
  526.  *      Allocate a channel for an RPC.  A pointer to the channel is
  527.  *      returned.  The allocation is done on the basis of the server
  528.  *      machine involved.  The goal is to send a long series of RPC
  529.  *      requests to the same server over the same channel.  To that end a
  530.  *      channel is chosen first if its cached address matches the input
  531.  *      server address.  Second choice is a previously unused channel,
  532.  *      lastly we re-use a channel that has been used with a different server.
  533.  *
  534.  * Results:
  535.  *    A pointer to the channel.  This returns a good pointer, or it panics.
  536.  *
  537.  * Side effects:
  538.  *    The channel is dedicated to the RPC until the caller frees
  539.  *    the channel with RpcChanFree.
  540.  *
  541.  *----------------------------------------------------------------------
  542.  */
  543. ENTRY RpcClientChannel *
  544. RpcChanAlloc(serverID)
  545.     int serverID;    /* Server ID to base our allocation on. */
  546. {
  547.     RpcClientChannel *chanPtr = (RpcClientChannel *) NULL;
  548.                     /* The channel we allocate */
  549.     register int i;            /* Index into channel table */
  550.     int firstUnused = -1;        /* The first unused channel */
  551.     int firstBusy = -1;            /* The first busy channel for server */
  552.     int firstFreeMatch = -1;        /* The first chan free for server */
  553.     int firstFree = -1;            /* The first chan used but now free */
  554.     Timer_Ticks    time;            /* When server channel state set. */
  555.     Timer_Ticks    currentTime;        /* Current ticks. */
  556.     Boolean    result;            /* Result of function call. */
  557.  
  558.     MASTER_LOCK(&rpcMutex);
  559.  
  560.     while (numFreeChannels < 1) {
  561.     rpcCltStat.chanWaits++;
  562. waitForBusyChannel:
  563.     Sync_MasterWait(&freeChannels, &rpcMutex, FALSE);
  564.     }
  565.     firstUnused = -1;
  566.     firstBusy = -1;
  567.     firstFreeMatch = -1;
  568.     firstFree = -1;
  569.  
  570.     result = GetChannelAllocState(serverID, &time);
  571.     if (result) {
  572.     Timer_AddIntervalToTicks(time, channelStateInterval, &time);
  573.     Timer_GetCurrentTicks(¤tTime);
  574.     }
  575.     if (result && (Timer_TickGE(time, currentTime))) {
  576.     /*
  577.      * Server is congested, so ramp down use of channels.
  578.      * If there's a channel, free or busy, for our server, use the
  579.      * lowest numbered one.  If it's busy, wait till it's not.  If there's
  580.      * no channel for this server, take free one.
  581.      */
  582.     for (i=0 ; i<rpcNumChannels ; i++) {
  583.         chanPtr = rpcChannelPtrPtr[i];
  584.         if (serverID == chanPtr->serverID) {
  585.         if (chanPtr->state == CHAN_FREE) {
  586.             if (firstFreeMatch < 0) {
  587.             firstFreeMatch = i;
  588.             }
  589.         } else {
  590.             if (firstBusy < 0) {
  591.             firstBusy = i;
  592.             }
  593.         }
  594.         } else if (chanPtr->serverID == -1) {
  595.         if (firstUnused < 0) {
  596.             firstUnused = i;
  597.         }
  598.         } else {
  599.         if (chanPtr->state == CHAN_FREE) {
  600.             if (firstFree < 0)  {
  601.             firstFree = i;
  602.             }
  603.         }
  604.         }
  605.     }
  606.     if (firstFreeMatch >= 0 &&
  607.         (firstBusy == -1 || firstBusy > firstFreeMatch)) {
  608.         /*
  609.          * We've found a free channel matching our server ID and there
  610.          * isn't a busy one for our server ID of lower number, so take
  611.          * this one.
  612.          */
  613.         chanPtr = rpcChannelPtrPtr[firstFreeMatch];
  614.         goto found;
  615.     }
  616.     if (firstBusy > 0) {
  617.         /*
  618.          * There's a busy channel matching our server ID and either it's
  619.          * of lower number than a free channel matching our serverID or
  620.          * else there's no free channel matching our server ID.  Wait for
  621.          * this one to free up.
  622.          */
  623.         rpcCltStat.nackChanWait++;
  624.         goto waitForBusyChannel;
  625.     }
  626.     /*
  627.      * Otherwise, there's no free our busy channel matching our server ID
  628.      * so we'll create one in the code later down below.
  629.      */
  630.     } else {
  631.     /*
  632.      * Server is not congested.  Make sure it's marked okay, and allocate
  633.      * a channel in the regular fasion.
  634.      */
  635.     if (result) {
  636.         /* Mark server as okay now. */
  637.         SetChannelAllocStateInt(serverID, FALSE);
  638.     }
  639.     /* use regular alloc */
  640.  
  641.     for (i=0 ; i<rpcNumChannels ; i++) {
  642.         chanPtr = rpcChannelPtrPtr[i];
  643.         if (chanPtr->state == CHAN_FREE) {
  644.         if (chanPtr->serverID == -1) {
  645.             /*
  646.              * Remember the first unused channel.
  647.              */
  648.             if (firstUnused < 0) {
  649.             firstUnused = i;
  650.             }
  651.         } else if (serverID == chanPtr->serverID) {
  652.             /*
  653.              * Agreement between the channels old server and the
  654.              * server ID.  By reusing this channel we hope to give
  655.              * the server an implicit acknowledgment for the
  656.              * previous transaction.
  657.              */
  658.             rpcCltStat.chanHits++;
  659.             CHAN_TRACE(chanPtr, "alloc channel w/ same server");
  660.             goto found;
  661.         } else if (firstFree < 0) {
  662.             /*
  663.              * The first free channel (with some server) is less of
  664.              * a good candidate for allocation.
  665.              */
  666.             firstFree = i;
  667.         }
  668.         }
  669.     }
  670.     }
  671.     /*
  672.      * We didn't find an address match on a free channel, so we use
  673.      * the first previously unused channel or the first used but free channel.
  674.      */
  675.     if (firstUnused >= 0) {
  676.     rpcCltStat.chanNew++;
  677.     chanPtr = rpcChannelPtrPtr[firstUnused];
  678.     CHAN_TRACE(chanPtr, "alloc first unused");
  679.     } else if (firstFree >= 0) {
  680.     rpcCltStat.chanReuse++;
  681.     chanPtr = rpcChannelPtrPtr[firstFree];
  682.     CHAN_TRACE(chanPtr, "alloc first free");
  683.     } else {
  684.     panic("Rpc_ChanAlloc can't find the free channel.\n");
  685.     }
  686.     chanPtr->serverID = serverID;
  687.     chanPtr->constPtr = (RpcConst *)NIL;    /* Set in RpcSetup */
  688.  
  689. found:
  690.     chanPtr->state = CHAN_BUSY;
  691.     numFreeChannels--;
  692.  
  693.     MASTER_UNLOCK(&rpcMutex);
  694.     return(chanPtr);
  695. }
  696.  
  697. /*
  698.  *----------------------------------------------------------------------
  699.  *
  700.  * RpcChanFree --
  701.  *
  702.  *    Free an RPC channel.
  703.  *
  704.  * Results:
  705.  *    None.
  706.  *
  707.  * Side effects:
  708.  *    The channel is available for other RPC calls.
  709.  *
  710.  *----------------------------------------------------------------------
  711.  */
  712. ENTRY void
  713. RpcChanFree(chanPtr)
  714.     RpcClientChannel *chanPtr;        /* The channel to free */
  715. {
  716.  
  717.     MASTER_LOCK(&rpcMutex);
  718.     CHAN_TRACE(chanPtr, "free channel");
  719.     if (chanPtr->state == CHAN_FREE) {
  720.     panic("Rpc_ChanFree: freeing free channel\n");
  721.     }
  722.     chanPtr->state = CHAN_FREE;
  723.  
  724.     numFreeChannels++;
  725.     if (numFreeChannels == 1 || rpcChannelNegAcks) {
  726.     rpcCltStat.chanBroads++;
  727.     Sync_MasterBroadcast(&freeChannels);
  728.     }
  729.     
  730.     MASTER_UNLOCK(&rpcMutex);
  731. }
  732.  
  733. /*
  734.  *----------------------------------------------------------------------
  735.  *
  736.  * RpcChanClose --
  737.  *
  738.  *    Process a close request from a server. This is called from
  739.  *    RpcClientDispatch at interrupt level.  If the channel is not
  740.  *    busy then the interrupt handler
  741.  *    needs to mark the channel as busy momentarily and send an
  742.  *    ack to the server. See RpcClientDispatch for more details.
  743.  *
  744.  * Results:
  745.  *    None.
  746.  *
  747.  * Side effects:
  748.  *    The channel might be used to send an ack to the server.
  749.  *
  750.  *----------------------------------------------------------------------
  751.  */
  752.  
  753. void
  754. RpcChanClose(chanPtr,rpcHdrPtr)
  755.     register RpcClientChannel *chanPtr;    /* The channel to use.*/
  756.     register RpcHdr *rpcHdrPtr;        /* The Rpc header as it sits in the
  757.                      * network module's buffer.  The data
  758.                      * in the message follows this. */
  759. {
  760.     register RpcHdr *ackHdrPtr;
  761.     if ((chanPtr->state & CHAN_BUSY) == 0) {
  762.     MASTER_LOCK(&rpcMutex);
  763.     /*
  764.      * Check again to make sure the channel isn't busy,
  765.      * then temporarily allocate it while we issue the explicit ack.
  766.      * If a process has slipped in and allocated the channel then its
  767.      * request will serve as the acknowledgement and we can bail out.
  768.      */
  769.     if ((chanPtr->state & CHAN_BUSY) != 0) {
  770.         MASTER_UNLOCK(&rpcMutex);
  771.         return;
  772.     }
  773.     chanPtr->state |= CHAN_BUSY;
  774.     numFreeChannels--;
  775.     MASTER_UNLOCK(&rpcMutex);
  776.  
  777.     rpcCltStat.close++;
  778.     /*
  779.      * Set up and transmit the explicit acknowledgement packet.
  780.      * Note that fields that never change have already been
  781.      * set in RpcBufferInit.
  782.      */
  783.     ackHdrPtr = &chanPtr->ackHdr;
  784.     ackHdrPtr->flags = RPC_ACK | RPC_CLOSE | RPC_SERVER;
  785.     ackHdrPtr->clientID = rpc_SpriteID;
  786.     ackHdrPtr->serverID = rpcHdrPtr->serverID;
  787.     ackHdrPtr->serverHint = rpcHdrPtr->serverHint;
  788.     ackHdrPtr->command = rpcHdrPtr->command;
  789.     ackHdrPtr->bootID = rpcBootID;
  790.     ackHdrPtr->ID = rpcHdrPtr->ID;
  791.     (void)RpcOutput(rpcHdrPtr->serverID, &chanPtr->ackHdr, &chanPtr->ack,
  792.                  (RpcBufferSet *)NIL, 0, (Sync_Semaphore *)NIL);
  793.     /*
  794.      * Note that the packet can linger in the network output queue,
  795.      * so we are relying on the fact that it would only be reused
  796.      * for another ack in the worst case.
  797.      */
  798.  
  799.     MASTER_LOCK(&rpcMutex);
  800.     chanPtr->state &= ~CHAN_BUSY;
  801.     numFreeChannels++;
  802.     MASTER_UNLOCK(&rpcMutex);
  803.     }
  804. }
  805.  
  806.  
  807. /*
  808.  *----------------------------------------------------------------------
  809.  *
  810.  * RpcInitServerChannelState --
  811.  *
  812.  *    Initialize data about the client's view of how the servers are doing.
  813.  *
  814.  * Results:
  815.  *    None.
  816.  *
  817.  * Side effects:
  818.  *    None.
  819.  *
  820.  *----------------------------------------------------------------------
  821.  */
  822. void
  823. RpcInitServerChannelState()
  824. {
  825.     int        i;
  826.  
  827.     for (i = 0; i < (sizeof (serverAllocState) / sizeof (UnhappyServer)); i++) {
  828.     serverAllocState[i].serverID = -1;
  829.     serverAllocState[i].time = timer_TicksZeroSeconds;
  830.     }
  831.     channelStateInterval = timer_IntOneSecond * 10;
  832. }
  833.